home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
How to Get Online 1996 Spring
/
HOW2GON.ISO
/
mac
/
Servers & CGI
/
WebStat 2.3.4 ƒ
/
WebStat.cp
< prev
next >
Wrap
Text File
|
1994-10-11
|
33KB
|
1,240 lines
/*
** File: WebStat.cp
**
** A transmission statistics summary program for MacHTTP log output
**
** Author: Philip Harvey (phil@nsun.phy.queensu.ca)
** Physics Dept
** Queen's University
**
** Revisions: 03/24/94 - V 1.0 - original
** 03/25/94 - V 1.1 - fixed array indexing problem
** 03/28/94 - V 1.2 - added anchors for lists; converted code to C++
** 03/29/94 - V 1.3 - added exclusion option and application icon
** - changed all byte counts from long to double
** 03/30/94 - V 1.4 - changed subdomain list to not reverse numerical entries
** - placed numerical entries at end of list
** 03/31/94 - V 1.5 - added config file and moved exclusions into config
** 04/04/94 - V 1.6 - added ADDRESSES Long/Short option
** - changed MESSAGES syntax from True/False to On/Off
** - added DOMAIN command to config file
** 04/08/94 - V 1.7 - added comprehensive list of Domain names to config file
** - changed spacing to accomodate longer domain names
** 04/11/94 - V 1.7.1 - delete trailing '/' if found in file names
** 04/14/94 - V 1.8 - ignored improperly formatted lines in log file
** 05/13/94 - V 1.9 - added SUMMARIZE options; changed to Symantec C++ 7.0
** - fixed quirk where days with no transfers were ignored
** 06/06/94 - V 2.0 - truncate filenames at '$' because of new cgi formatting
** - added check on time_t values passed to GetNumDays()
** - added format file (replaces SUMMARIZE option of V1.9)
** 08/11/94 - V 2.1 - removed limit on number of EXCLUDE and DOMAIN statements
** - sort numerical addresses properly
** 09/27/94 - V 2.2 - added DNSLOOKUP option
** 09/28/94 - V 2.3 - added progress indicator if MESSAGES Off
** - allows background processing if MESSAGES Off
** - fixed small bug in DNS lookups
** - Cmd-. aborts processing if MESSAGES Off
** - MESSAGES Off is now the default
** 09/28/94 - V 2.3.1 - fixed problem in event handling during DNS lookups
** - fixed problem with output going to system folder broken in 2.3
** 10/02/94 - V 2.3.2 - changed progress indicator; added stop button
** - re-fixed small bug in DNS lookups lost in 2.3.1
** 10/03/94 - V 2.3.3 - fixed problem which could cause crash if stop button pressed
** - message dialog box is now properly activated
** 10/11/94 - V 2.3.4 - progress bar now displays properly on B&W screens
**
** Notes: For updates, the version number must be changed in the progress dialog title, in
** the program_name variable, and 3 times in the 'vers' resource.
*/
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "WebStat.h"
#include "TCPLib.h"
enum {
kStopButtonItem = 1,
kStaticTextItem,
kProgressIndicatorItem
};
extern "C" void HandleEvents(void);
/*
** static string definitions
*/
static char * config_file = "WebStat.config";
static char * def_log_file = "MACHTTP.LOG";
static char * def_out_file = "WebStat.html";
static char * def_fmt_file = "WebStat.format";
static char * program_name = "WebStat 2.3.4";
/*
** The following constants specify the number of array elements by which the
** statistics lists should grow each time more memory is required:
**
** If the numbers are too small memory may become fragmented due to excess
** reallocations and the program will run more slowly, and if the numbers are
** too large more memory than necessary will be used.
**
** If memory and/or speed are a concern, these numbers can be optimized for
** your individual needs.
*/
const long kDateIncr = 1000; // for date list (this will do for 3 years)
const long kHourIncr = 24; // for hour list (only 24 hours in a day)
const long kWDayIncr = 7; // for weekday list (7 days in a week)
const long kDomainIncr = 200; // for domain list (alot of countries)
const long kSubDomainIncr = 5000; // for subdomain list (alot of computers)
const long kFileIncr = 5000; // for archive file list (alot of files)
const long kExcludeIncr = 100; // for list of exclusions (EXCLUDE statements)
const long kCountryIncr = 500; // for country list (DOMAIN statements)
const long kDNSListIncr = 500; // for DNS lookup list
/*
** definitions of static member variables
*/
unsigned long StatList::totalFiles = 0;
double StatList::totalBytes = 0;
/*-----------------------------------------------------------------------------------------
** Utility routines
*/
/* strcmpi - compare two strings, ignoring case, and sorting special characters last */
static int strcmpi(const char *str1, const char *str2)
{
char c1,c2;
for (;; ++str1, ++str2) {
c1 = toupper(*str1);
c2 = toupper(*str2);
if (c1==c2) {
if (c1) continue;
break;
}
if (c1 < c2) {
if (!isupper(c1) && isupper(c2)) return(1);
else return(-1);
}
if (c1 > c2) {
if (isupper(c1) && !isupper(c2)) return(-1);
else return(1);
}
}
return(0);
}
/* strcmpiPart - compare two strings, ignoring case, and stopping at the end of str1 */
static int strcmpiPart(const char *str1, const char *str2)
{
char c1,c2;
for (;; ++str1, ++str2) {
c1 = toupper(*str1);
c2 = toupper(*str2);
if (!c1) break;
if (c1 < c2) return(-1);
if (c1 > c2) return(1);
}
return(0);
}
/* strcmpiWild - compare two strings, ignoring case, allow wildcards */
static int strcmpiWild(const char *str1, const char *str2)
{
char c1,c2;
for (;;) {
c1 = toupper(*str1);
c2 = toupper(*str2);
if (c1=='*' || c2=='*') return(0);
if (c1 < c2) return(-1);
if (c1 > c2) return(1);
if (!c1) break;
++str1;
++str2;
}
return(0);
}
/* strtokQuote - strtok routine with quoted strings allowed */
static char *strtokQuote(char *str, char *delim)
{
char *term;
static char *next=0;
if (!str && ((str=next)==0 || !(*str))) return((char *)0);
/* skip all delimiter characters */
for (; *str; ++str) if (!strchr(delim,*str)) break;
/* are we done? */
if (!*str) return(next=(char *)0);
/* check for start of quoted string */
if (*str == '"') {
term = strchr(++str,'"'); // terminate at matching quote
} else {
/* find next delimiter */
for (term=str+1; *term; ++term) if (strchr(delim,*term)) break;
}
if (term && *term) {
*term = 0; // null terminate this token
next = term + 1;
} else {
next = 0;
}
return(str);
}
/*------------------------------------------------------------------------------------------
** Mac routines
*/
static Rect progressItemRect;
static long progressItemValue = -1;
static Boolean macMode = FALSE;
static Boolean wne_impl;
static Rect dragRect;
static void ToolBoxInit(void)
{
const int WNE_TRAP_NUM = 0x60;
const int UNIMPL_TRAP_NUM = 0x9f;
macMode = TRUE;
InitGraf(&thePort);
InitFonts();
FlushEvents(everyEvent,0);
InitWindows();
InitMenus();
TEInit();
InitDialogs((ResumeProcPtr)0);
InitCursor();
wne_impl = (NGetTrapAddress(WNE_TRAP_NUM,ToolTrap) !=
NGetTrapAddress(UNIMPL_TRAP_NUM,ToolTrap));
dragRect = screenBits.bounds;
InsetRect(&dragRect, 4, 4);
}
/* DrawMyItem - user item routine for progress indicator of wait dialog */
static void pascal DrawMyItem(WindowPtr theWindow, short itemNum)
{
int tmp;
Rect rect;
static RGBColor colors[] = { { 0xcccc, 0xcccc, 0xffff }, // Baby blue
{ 0x4444, 0x4444, 0x4444 }, // DarkGrey
};
/* copy the item rect */
rect = progressItemRect;
/* draw a black frame */
FrameRect(&rect);
/* draw the indicator background */
InsetRect(&rect,1,1);
tmp = rect.left;
rect.left += (int)((rect.right-rect.left) * progressItemValue / 100);
RGBBackColor(colors);
EraseRect(&rect);
/* draw the indicated percentage */
RGBForeColor(colors + 1);
rect.right = rect.left;
rect.left = tmp;
PaintRect(&rect);
}
/* ShowProgress - show progress dialog */
/* (if percent<0 dialog is removed) */
void ShowProgress(int percent, Boolean forceShow)
{
short itemType;
Handle itemHand, dlogHandle;
long tmpLong;
SignedByte savedState;
static DialogPtr progressDlg = 0;
const long kProgressDlgID = 200L;
const long kWaitTime = 10;
static long nextTime = 0;
Rect rect;
GrafPtr oldPort;
if (percent < 0) {
/* dispose the dialog if it exists */
if (progressDlg) {
DisposDialog(progressDlg);
progressDlg = 0;
progressItemValue = -1;
nextTime = 0;
}
} else {
/* create the dialog if it doesn't exist */
if (!progressDlg) {
/* must lock resource so it isn't purged in GetNewDialog (!!) */
/* (if it is purged, the PositionDialog() call would be undone) */
dlogHandle = GetResource('DLOG', kProgressDlgID);
savedState = HGetState(dlogHandle);
HLock(dlogHandle);
/* position dialog and get handle */
progressDlg = GetNewDialog(kProgressDlgID,NULL,(WindowPtr)-1L);
/* restore the resource memory state */
HSetState(dlogHandle, savedState);
/* set the drawing proc for the progress indicator item */
GetDItem(progressDlg,kProgressIndicatorItem,&itemType,&itemHand,&progressItemRect);
SetDItem(progressDlg,kProgressIndicatorItem,itemType,(Handle)DrawMyItem,&progressItemRect);
}
if (forceShow || (progressItemValue!=percent && TickCount()>nextTime)) {
/* set the progress value */
progressItemValue = percent;
/* set the next time to update this indicator */
nextTime = TickCount() + kWaitTime;
if (forceShow) {
/* draw the whole dialog (to update text too) */
DrawDialog(progressDlg);
} else {
/* update the progress indicator only */
GetPort(&oldPort);
SetPort(progressDlg);
/* invalidate the progress item */
InvalRect(&progressItemRect);
SetPort(oldPort);
}
}
}
}
/* Quit - Quit program */
static void Quit(unsigned char *str)
{
unsigned char *pt;
const int kMsgAlertID = 201;
const int kLineLen = 50;
if (macMode) {
ShowProgress(-1,TRUE);
ParamText(str,"\p","\p","\p");
NoteAlert(kMsgAlertID,0);
ExitToShell();
} else {
++str;
while (*pt) {
pt = (unsigned char *)strchr((char *)str,'\r');
if (!pt) pt = (unsigned char *)strchr((char *)str,'\0');
/* keep lines to 80 chars or less */
if (pt - str > kLineLen) {
pt = str + kLineLen;
while (pt>str && !isspace(*pt)) --pt;
if (pt == str) pt = str + kLineLen;
}
printf("%.*s\n",pt-str,str);
if (*pt == '\r') printf("\n");
str = pt + 1;
}
exit(1);
}
}
/* HandleEvents - process Mac events */
void HandleEvents()
{
EventRecord theEvent;
short thePart;
WindowPtr whichWindow;
DialogPtr theDialog;
Boolean abort = FALSE;
/* process mac events */
if (wne_impl) {
WaitNextEvent(everyEvent, &theEvent, 0L, 0L);
} else {
SystemTask();
GetNextEvent(everyEvent, &theEvent);
}
/* does the user want to abort? */
if (theEvent.what==keyDown && theEvent.modifiers&cmdKey && (theEvent.message&0xff)=='.') {
abort = TRUE;
} else if (IsDialogEvent(&theEvent)) {
if (DialogSelect(&theEvent, &theDialog, &thePart)) {
if (thePart == kStopButtonItem) abort = TRUE;
}
} else if (theEvent.what == mouseDown) {
/* handle window drags */
thePart = FindWindow(theEvent.where, &whichWindow);
if (thePart == inDrag) {
SelectWindow(whichWindow);
DragWindow(whichWindow, theEvent.where, &dragRect);
}
}
if (abort) {
/* must close net to avoid problems if */
/* quitting in the middle of a DNS lookup */
CloseResolver();
Quit("\pWebStat processing halted!\rOutput file not written.\0");
}
}
/*------------------------------------------------------------------------------------------
** Other routines
*/
/* MemoryError - print memory error message and terminate program */
static void MemoryError()
{
Quit("\pNot enough memory!\rPlease increase the memory size for WebStat using the Get Info option in the File menu.\0");
}
/* ReverseAddress - reverse an IP address string */
/* - kills source string */
/* - returns pointer to last field in addr */
static char *ReverseAddress(char *src, char *dst)
{
char *pt;
char *last_field = 0;
dst[0] = 0;
while ((pt=strrchr(src,'.')) != 0) {
if (!last_field) last_field = pt + 1;
strcat(dst,pt+1);
strcat(dst,".");
*pt = 0;
}
strcat(dst,src);
return(last_field);
}
/* GetIPAddress - get numerical address from string */
static long GetIPAddress(char *hostname)
{
int i;
long addr = 0;
for (i=3;;) {
addr |= (long)atoi(hostname) << 8*i;
if (--i < 0) break;
hostname = strchr(hostname,'.') + 1;
if (!hostname) return(0L);
}
return(addr);
}
/* ConvTime - get struct tm from time and date strings */
/* Time and date are in the form "09:57:56" and "03/28/94" */
/* otherwise a zero is returned. */
static time_t ConvTime(char *time, char *date, struct tm *tms)
{
/* do a quick check on the time and date formats so we can be */
/* resonably certain that we are looking at the correct strings */
if (time[2]!=':' || date[2]!='/') return((time_t)0);
tms->tm_sec = atoi(time+6);
tms->tm_min = atoi(time+3);
tms->tm_hour = atoi(time);
tms->tm_mday = atoi(date+3);
tms->tm_mon = atoi(date) - 1;
tms->tm_year = atoi(date+6);
tms->tm_isdst = 0;
return(mktime(tms));
}
/* ConvDate - convert date of form "03/28/94" to "Mar 28 1994" */
static char *ConvDate(const char *date)
{
static char buff[100];
static char * months = "JanFebMarAprMayJunJulAugSepOctNovDec";
sprintf(buff,"%3.3s %2d %4d",
months+3*(atoi(date)-1),
atoi(date + 3),
atoi(date + 6) + 1900);
return(buff);
}
/* GetDateStr - get date string from ctime() output */
static void GetDateStr(char *out, char *ctimeStr)
{
memcpy(out, ctimeStr+4, 7); // copy month and day
memcpy(out+7, ctimeStr+20, 4); // copy year
out[11] = 0; // null terminate it
}
/* GetNumDays - get the number of days between two time_t values */
static long GetNumDays(time_t start, time_t end)
{
struct tm *tms;
int day1, year1;
int day2, year2;
long numDays;
if (start >= end) return(1); // exit gracefully on bad input
tms = localtime(&start);
day1 = tms->tm_yday;
year1 = tms->tm_year;
tms = localtime(&end);
day2 = tms->tm_yday;
year2 = tms->tm_year;
numDays = day2 - day1 + 1;
while (year1 < year2) {
if (year1 % 4 != 0) numDays += 365L;
else if (year1 % 100 != 0) numDays += 366L; // leap year
else numDays += 365L; // 365 days at turn of century
++year1;
}
if (numDays < 1) numDays = 1;
return(numDays);
}
/*-----------------------------------------------------------------------------------------
** Comparison routines
*/
/* CompStat - compare two statistics string names (used in qsort()) */
int CompStat(const void *p1, const void *p2)
{
char *str1 = ((Statistic *)p1)->name;
char *str2 = ((Statistic *)p2)->name;
return(strcmpi(str1,str2));
}
/* CompStatNum - compare two statistics string names, evaluating numerical addresses */
int CompStatNum(const void *p1, const void *p2)
{
int n1, n2;
char *str1 = ((Statistic *)p1)->name;
char *str2 = ((Statistic *)p2)->name;
if (isdigit(*str1) && isdigit(*str2)) {
for (;;) {
n1 = atoi(str1);
n2 = atoi(str2);
if (n1 == n2) {
str1 = strchr(str1,'.');
if (!str1) return(0);
str2 = strchr(str2,'.');
if (!str2) return(0);
++str1;
++str2;
} else if (n1 < n2) {
return(-1);
} else {
return(1);
}
}
} else {
return(strcmpi(str1,str2));
}
}
/*--------------------------------------------------------------------------------------
** StringLookup class methods
*/
/* StringLookup - class constructor */
StringLookup::StringLookup(long incr, Boolean part)
: num(0),maxNum(0),incrNum(incr),partialCompare(part)
{
}
/* ~StringLookup - class destructor */
StringLookup::~StringLookup()
{
long i;
/* reset name list */
if (maxNum) {
for (i=0; i<num; ++i) delete list[i];
maxNum = num = 0;
delete list;
}
}
/* AddString - add string to lookup list */
void StringLookup::Add(char *code, char *name)
{
int i;
char **oldList;
/* expand country list if necessary */
if (num >= maxNum) {
oldList = list;
list = new char *[maxNum + incrNum];
if (!list) MemoryError();
if (maxNum) {
memcpy(list, oldList, maxNum * sizeof(char *));
delete oldList;
}
maxNum += incrNum;
}
/* add country to list */
i = strlen(code) + strlen(name) + 2;
list[num] = new char[i];
if (!list[num]) MemoryError();
strcpy(list[num],code);
strcpy(strchr(list[num],'\0')+1,name);
++num;
}
/* Lookup - look up an entry in the list */
char *StringLookup::Lookup(char *code)
{
long i;
/* search for entry in list */
for (i=0; i<num; ++i) {
if (partialCompare) {
if (!strcmpiPart(list[i], code)) return(strchr(list[i],'\0')+1);
} else {
if (!strcmpi(list[i], code)) return(strchr(list[i],'\0')+1);
}
}
return((char *)0);
}
/*-----------------------------------------------------------------------------------------
** Statistic class methods
*/
/* Statistic - constructor */
Statistic::Statistic() : files(0), bytes(0), name(0)
{
/* empty method */
}
/* ~Statistic - destructor */
Statistic::~Statistic()
{
if (name) delete name; // free memory for name
}
/* IncrStats - increment statistics counters */
void Statistic::IncrStats(double byte_count)
{
files++;
bytes += byte_count;
}
/*-----------------------------------------------------------------------------------------
** StatList class methods
*/
/* StatList - constructor */
StatList::StatList(long iSize) : num(0)
{
incrSize = maxNum = iSize;
stat = new Statistic[iSize];
if (!stat) MemoryError();
}
/* ~StatList - destructor */
StatList::~StatList()
{
/* delete all Statistic objects */
for (long n=0; n<maxNum; ++n) delete(stat+n);
}
/* Write - write list to file */
void StatList::Write(FILE *fp, char *heading)
{
int i,len;
long n;
double totFile, totByte;
const int BSIZ = 256;
char *name, buff[BSIZ];
len = strlen(heading);
if (len > BSIZ) return;
memset(buff,'-',len);
buff[len] = 0;
if (!totalFiles) totFile = 1; // avoid divide by zero
else totFile = totalFiles / 100.0;
if (!totalBytes) totByte = 1;
else totByte = totalBytes / 100.0;
fprintf(fp,"<pre>\n");
fprintf(fp,"%*s Number of Number of Percent of Percent of\n",len,"");
fprintf(fp, "%s Files Sent Bytes Sent Files Sent Bytes Sent\n",heading);
fprintf(fp, "%s ---------- ------------ ---------- ----------\n",buff);
++len;
for (n=0; n<num; ++n) {
if ((i=strlen(stat[n].name))<=len || len<3) {
name = stat[n].name;
} else {
strcpy(buff,"...");
strcat(buff,stat[n].name+i-len+3);
name = buff;
}
fprintf(fp,"%-*s %10ld %13.0f %6.2f %6.2f\n",
len,
name,
stat[n].files,
stat[n].bytes,
stat[n].files / totFile,
stat[n].bytes / totByte);
}
fprintf(fp,"</pre><p>\n");
}
/* SearchStat - search list for matching string, creating new entry if not found */
long StatList::SearchStat(char *str)
{
long n;
Statistic *tempStat;
/* search through stat names (most recent first for speed) */
for (n=num-1; n>=0; --n) {
if (!strcmpi(str,stat[n].name)) return(n);
}
if (num >= maxNum) {
tempStat = new Statistic[maxNum + incrSize];
if (!tempStat) MemoryError();
for (n=0; n<maxNum; ++n) {
tempStat[n] = stat[n];
stat[n].name = 0; // make sure memory is not deallocated for name
}
delete stat;
stat = tempStat;
maxNum += incrSize;
}
stat[num].name = new char[strlen(str)+1];
if (!stat[num].name) MemoryError();
strcpy(stat[num].name,str);
return(num++);
}
/* IncrStats - increment the stats for given string */
void StatList::IncrStats(double byte_count, char *str)
{
long n;
n = SearchStat(str);
stat[n].IncrStats(byte_count); // increment individual sums
}
/* IncrTotals - increment total counters */
void StatList::IncrTotals(double byte_count)
{
totalFiles++; // increment totals
totalBytes += byte_count;
}
/* Substitute - substitute names in list */
void StatList::Substitute(char *(*func)(const char *))
{
char *pt, *pt2;
for (long n=0; n<num; ++n) {
pt = func(stat[n].name); // get new string
if (pt) {
pt2 = new char[strlen(pt) + 1];
if (!pt2) MemoryError();
strcpy(pt2,pt); // copy the string
delete stat[n].name; // get rid of old string
stat[n].name = pt2;
}
}
}
/* Sort - sort the list alphabetically */
void StatList::Sort(CompFunc func)
{
if (!func) func = CompStat;
qsort(stat, num, sizeof(Statistic), func);
}
/* Summarize - print summary of total day statistics */
void StatList::Summarize(FILE *fp, time_t first, time_t last)
{
char firstStr[30];
char lastStr[30];
long num;
num = GetNumDays(first, last);
GetDateStr(firstStr, ctime(&first));
GetDateStr(lastStr, ctime(&last));
fprintf(fp,"<h2>Summary for Period: %s to %s</h2>\n", firstStr, lastStr);
fprintf(fp,"<pre>\n");
fprintf(fp,"Files Transmitted During Summary Period %14d\n",totalFiles);
fprintf(fp,"Bytes Transmitted During Summary Period %14.0f\n",totalBytes);
fprintf(fp,"Average Files Transmitted Daily %14d\n",totalFiles/num);
fprintf(fp,"Average Bytes Transmitted Daily %14.0f\n",totalBytes/num);
fprintf(fp,"</pre><p>\n");
}
/*-----------------------------------------------------------------------------------------
** main program
*/
void main()
{
long i, line, fileSize;
long numExcl = 0, maxExcl = 0;
long numCtry = 0;
char *logFile = def_log_file;
char *outFile = def_out_file;
char *fmtFile = def_fmt_file;
Boolean messages = FALSE;
Boolean long_addr = TRUE;
Boolean dnsLookup = FALSE;
Boolean isReverse;
time_t tt, firstTime, lastTime;
struct tm tms;
char *pt,*pt2, *pt3, *country, *last_field;
char *domain,*file,*time_str,*date_str,*stat_str;
char **exclude, **oldExcl;
const int BSIZ = 512;
Str255 curVol;
short vRefNum;
char rev_domain[BSIZ],buff[BSIZ],dns_name[BSIZ],buf2[BSIZ];
StringLookup *countryList, *addressList;
double bytes;
StatList *byDate, *byHour, *byWeekday, *byDomain, *bySubdomain, *byFile;
FILE *fp, *out, *fmt;
static char *syntaxFmt = "Syntax error in %s line %ld: %s '%s'\n";
static char *delim = " \t\n";
static char *weekdays[] = { "Sunday","Monday","Tuesday","Wednesday",
"Thursday","Friday","Saturday" };
firstTime = 0;
/* initialize country list object */
countryList = new StringLookup(kCountryIncr,TRUE);
if (!countryList) MemoryError();
/* read config file */
if ((fp = fopen(config_file,"r")) == 0) {
printf("Config file %s not found!\nUsing defaults.\n",config_file);
} else {
for (line=1; fgets(buff,BSIZ,fp); ++line) {
pt = strtokQuote(buff,delim);
if (!pt || *pt=='#') continue;
pt2 = strtokQuote(NULL,delim);
if (!pt2) {
printf(syntaxFmt,config_file,line,"No parameters for",pt);
continue;
}
if (!strcmpi(pt,"LOG")) {
logFile = new char[strlen(pt2) + 1];
if (!logFile) MemoryError();
strcpy(logFile, pt2);
} else if (!strcmpi(pt,"OUTPUT")) {
outFile = new char[strlen(pt2) + 1];
if (!outFile) MemoryError();
strcpy(outFile, pt2);
} else if (!strcmpi(pt,"FORMAT")) {
fmtFile = new char[strlen(pt2) + 1];
if (!fmtFile) MemoryError();
strcpy(fmtFile, pt2);
} else if (!strcmpi(pt,"EXCLUDE")) {
if (numExcl >= maxExcl) {
oldExcl = exclude;
exclude = new char *[maxExcl + kExcludeIncr];
if (!exclude) {
printf("Too many exclusions\n");
MemoryError();
}
if (maxExcl) {
memcpy(exclude, oldExcl, maxExcl * sizeof(char *));
delete oldExcl;
}
maxExcl += kExcludeIncr;
}
exclude[numExcl] = new char[strlen(pt2) + 1];
if (!exclude[numExcl]) MemoryError();
strcpy(exclude[numExcl], pt2);
++numExcl;
} else if (!strcmpi(pt,"MESSAGES")) {
if (!strcmpi(pt2,"On")) messages = TRUE;
} else if (!strcmpi(pt,"ADDRESSES")) {
if (!strcmpi(pt2,"Short")) long_addr = FALSE;
} else if (!strcmpi(pt,"DOMAIN")) {
pt3 = strchr(pt2,'*');
if (pt3) *pt3 = 0; // get rid of '*' if it exists
pt3 = strtokQuote(NULL,delim);
if (!pt3) {
printf(syntaxFmt,config_file,line,"Too few parameters for",pt);
continue;
}
countryList->Add(pt2, pt3);
++numCtry;
} else if (!strcmpi(pt,"DNSLOOKUP")) {
if (!strcmpi(pt2,"On")) dnsLookup = TRUE;
} else {
printf(syntaxFmt,config_file,line,"Unknown keyword",pt);
}
}
fclose(fp);
}
if (messages) {
printf("\n*****************************\n");
printf("** --- %s --- **\n",program_name);
printf("** A utility for MacHTTP **\n");
printf("** by Phil Harvey **\n");
printf("*****************************\n\n");
if (numCtry) printf("%ld domain name%s registered.\n",numCtry,numCtry==1?"":"s");
if (numExcl) printf("%ld exclusion%s registered.\n",numExcl,numExcl==1?"":"s");
if (numCtry || numExcl) printf("\n");
}
/* open log file */
if ((fp = fopen(logFile,"r")) == 0) {
printf("Can't open log file %s !\n\n",logFile);
printf("The log file name can be specified in WebStat.config.\n");
printf("If no path is specified, then the log file\n");
printf("must be in the same folder as the WebStat program.\n");
exit(1);
}
/* open format file */
if ((fmt = fopen(fmtFile,"r")) == 0) {
printf("Can't open the HTML format file %s !\n\n",fmtFile);
printf("If no path is specified, then the format file\n");
printf("must be in the same folder as the WebStat program.\n");
exit(1);
}
if (!messages) {
ToolBoxInit();
ParamText("\pProcessing log file","\p","\p","\p");
ShowProgress(0,TRUE);
}
/* initialize lists */
byDate = new StatList(kDateIncr);
byHour = new StatList(kHourIncr);
byWeekday = new StatList(kWDayIncr);
byDomain = new StatList(kDomainIncr);
bySubdomain = new StatList(kSubDomainIncr);
byFile = new StatList(kFileIncr);
/* initialize weekday and hour names */
for (i=0; i<7; ++i) byWeekday->SearchStat(weekdays[i]);
for (i=0; i<24; ++i) {
sprintf(buf2," %.2ld",i);
byHour->SearchStat(buf2);
}
/* init MacTCP if doing DNS lookups */
if (dnsLookup) {
/* get current volume (DNS routines change this!) */
GetVol(curVol,&vRefNum);
/* initialize DNS lookup cache table */
addressList = new StringLookup(kDNSListIncr,FALSE);
if (!addressList) MemoryError();
if (messages) printf("Initializing MacTCP...\n");
if (InitNetwork() != noErr) {
Quit("\pNet init error!\rYou may want to set DNSLOOKUP Off in your WebStat.config file.\0");
}
}
/* read the log file */
if (messages) {
printf("Reading file %s...\n",logFile);
} else {
/* get size of file */
fseek(fp,0L,SEEK_END);
fileSize = ftell(fp);
if (!fileSize) ++fileSize; // prevent /0 errors
fseek(fp,0L,SEEK_SET);
}
while (fgets(buff,BSIZ,fp)) {
if (!messages) {
/* handle events */
HandleEvents();
/* update the progress indicator */
ShowProgress(ftell(fp)*100L/fileSize, FALSE);
}
date_str = strtok(buff,delim);
time_str = strtok(NULL,delim);
stat_str = strtok(NULL,delim);
domain = strtok(NULL,delim);
file = strtok(NULL,delim);
bytes = atof(strtok(NULL,delim));
/* ignore lines that don't have enough entries */
if (!file) continue;
/* convert the time and date strings */
tt = ConvTime(time_str, date_str, &tms);
/* ignore line if the date didn't convert properly */
if (!tt) continue;
/* save first and last times */
if (!firstTime) firstTime = tt;
lastTime = tt;
/* clean up address string and determine if it is reversed */
isReverse = TRUE;
if (isdigit(domain[0])) {
pt = strstr(domain,".in-addr"); // get rid of ".in-addr.arpa."
if (pt) *pt = 0;
else isReverse = FALSE; // NO_DNS option must be on (name not rev)
} else {
domain[strlen(domain)-1] = 0; // get rid of trailing '.'
}
/* reverse the subdomain if necessary */
if (isReverse) {
last_field = ReverseAddress(domain,rev_domain);
} else {
strcpy(rev_domain,domain);
}
/* resolve all numerical addresses if DNSLOOKUP is On */
if (dnsLookup && isdigit(rev_domain[0])) {
/* does this entry exist in address cache? */
pt = addressList->Lookup(rev_domain);
/* if not found, consult DNS for translation */
if (!pt) {
if (messages) printf("DNS lookup %s -> ",rev_domain);
/* get resolved address in buf2 */
if (IPAddrToName(GetIPAddress(rev_domain),dns_name) == noErr) {
dns_name[strlen(dns_name)-1] = 0; // get rid of trailing '.'
if (messages) printf("%s\n",dns_name);
/* must reverse the name returned by DNS */
last_field = ReverseAddress(dns_name, buf2);
/* add to name cache */
addressList->Add(rev_domain, buf2);
/* copy new name into domain string */
strcpy(rev_domain, buf2);
} else {
/* use unresolved name if host unknown */
addressList->Add(rev_domain, rev_domain);
if (messages) printf("<unknown>\n");
}
} else {
/* use the converted name from the cache */
strcpy(rev_domain, pt);
}
}
/* check for exclusions */
for (i=0; i<numExcl; ++i) {
if (!strcmpiWild(exclude[i],rev_domain)) break;
}
if (i != numExcl) continue;
/* parse filespec */
for (pt=file; *pt; ++pt) {
if (*pt==':') {
if (pt[1]) *pt = '/'; // convert ':' to '/'
else *pt = 0;
} else if (*pt=='?' || *pt=='$') {
*pt = 0; // truncate file names at '?' or '$'
break;
}
}
/* increment totals */
StatList::IncrTotals(bytes);
/* weekly totals */
byWeekday->IncrStats(bytes, weekdays[tms.tm_wday]);
/* hourly totals */
sprintf(buf2," %.2d",tms.tm_hour);
byHour->IncrStats(bytes, buf2);
/* daily totals */
byDate->IncrStats(bytes, date_str);
/* totals by domain */
country = countryList->Lookup(rev_domain);
if (!country) {
if (isdigit(rev_domain[0])) country = "[unresolved]";
else country = last_field;
}
byDomain->IncrStats(bytes, country);
/* shorten address if requested for subdomain list */
if (!long_addr) {
pt = strrchr(rev_domain,'.');
if (pt) *pt = 0;
}
/* totals by subdomain */
bySubdomain->IncrStats(bytes, rev_domain);
/* totals by file */
if (memcmp(stat_str,"OK",2)) pt = "[nonexistent files]";
else pt = file;
byFile->IncrStats(bytes, pt);
}
/* close log file */
fclose(fp);
/* convert date formats */
byDate->Substitute(ConvDate);
/* sort the stuff */
byDomain->Sort();
bySubdomain->Sort(CompStatNum);
byFile->Sort();
/* print results */
if (messages) {
printf("Writing file %s...\n",outFile);
} else {
ParamText("\pWriting output file","\p","\p","\p");
ShowProgress(100,TRUE);
}
/* reset current volume if necessary */
if (dnsLookup) SetVol(curVol,vRefNum);
/* open output file */
if ((out = fopen(outFile,"w")) == 0) {
Quit("\pCan't open output file!\rAre your sure the output folder exists, and is not locked?\0");
}
while (fgets(buff, BSIZ, fmt)) {
/* interpret special codes in format file */
if (buff[0] == '[') {
if (!strcmpiPart("[Summary]",buff)) {
time(&tt);
fprintf(out,"Generated by: %s<br>\n",program_name);
fprintf(out,"Last updated: %s<p>\n",ctime(&tt));
StatList::Summarize(out, firstTime, lastTime);
continue;
} else if (!strcmpiPart("[Day]",buff)) {
byDate->Write(out," Date ");
continue;
} else if (!strcmpiPart("[Hour]",buff)) {
byHour->Write(out,"Time");
continue;
} else if (!strcmpiPart("[Weekday]",buff)) {
byWeekday->Write(out," Day ");
continue;
} else if (!strcmpiPart("[Domain]",buff)) {
byDomain->Write(out," Domain Name ");
continue;
} else if (!strcmpiPart("[Subdomain]",buff)) {
bySubdomain->Write(out," Reversed Subdomain ");
continue;
} else if (!strcmpiPart("[Archive]",buff)) {
byFile->Write(out," Archive Section ");
continue;
}
}
/* no special code found, copy line to the output */
fputs(buff,out);
}
fclose(out);
fclose(fmt);
/* free up memory */
delete byDate;
delete byHour;
delete byWeekday;
delete byDomain;
delete bySubdomain;
delete byFile;
/* delete exclusions */
if (numExcl) {
for (i=0; i<numExcl; ++i) delete exclude[i];
delete exclude;
}
/* free country list */
delete countryList;
/* free DNS cache if allocated */
if (dnsLookup) delete addressList;
if (messages) {
printf("Done.\n");
} else {
ShowProgress(-1,TRUE);
}
}